package jalgebrava.group.groups;

import static jalgebrava.util.collections.IterableMixins.*;
import jalgebrava.group.Group;
import jalgebrava.group.GroupElement;
import jalgebrava.group.GroupUtil;
import jalgebrava.group.Subgroup;
import jalgebrava.group.Subset;

import java.util.HashSet;

import org.apache.log4j.Logger;


public class QuotientGroupFactory {
	private static final Logger logger = Logger.getLogger(QuotientGroupFactory.class);
	
	static final <G> Group<Subset<G>> mk(final Group<G> g, final Subgroup<G> h) {
		if (!h.isNormal()) {
			throw new RuntimeException("not a normal subgroup");
		}
		int size = g.size() / h.size();
		final HashSet<Subset<G>> cosets = new HashSet<Subset<G>>(2 * size);
		HashSet<G> handled = new HashSet<G>(2 * g.size());
		cosets.add(h);
		for (G x: h) {
			handled.add(x);
		}
		
		for (GroupElement<G> x: g.elements()) {
			if (handled.contains(x.g)) {
				continue;
			}
			logger.debug("Not handled yet: " + x);
			Subset<G> coset = GroupUtil.mul(x,h);
			logger.debug("Added coset: " + coset);
			
			cosets.add(coset);
			for (G y: coset) {
				handled.add(y);
			}
		}
		logger.debug("Number of cosets = " + cosets.size());
		return new AbstractGroup<Subset<G>>(g.getDescription()+"/"+h.toString(),
				asImmutable(cosets), size) {

			private Subset<G> locate(G x) {
				for (Subset<G> s: cosets) {
					if (s.contains(x)) {
						return s;
					}
				}
				throw new RuntimeException("Cosets not covering entire group: " + x);
			}
			
			@Override
			public Subset<G> unit() {
				return h;
			}

			@Override
			public Subset<G> inv(Subset<G> x) {
				return locate(g.inv(x.pick()));
			}

			@Override
			public Subset<G> mul(Subset<G> x, Subset<G> y) {
				return locate(g.mul(x.pick(), y.pick()));
			}

			@Override
			public boolean contains(Subset<G> x) {
				return cosets.contains(x);
			}
			
		};
	}
}
